package com.wss.lsl.test.driven.redis;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

import com.wss.lsl.test.driven.redis.util.ScriptLoad;
import com.wss.lsl.test.driven.util.UUIDHexGenerator;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Transaction;

/**
 * redis模版
 * 
 * @author Administrator
 */
public class RedisTemplate {

	// 释放分布式锁的lua脚本
	private static final ScriptLoad RELEASE_LOCK_LUA;
	// 复制有序集合的某已分值范围内的元素到一个集合中
	private static final ScriptLoad COPY_RANGE_TO_SET_LUA;

	static {
		RELEASE_LOCK_LUA = ScriptLoad
				.loadScript("if redis.call('get', KEYS[1]) == ARGV[1] then "
						+ " return redis.call('del', KEYS[1]) or true " + "end");

		COPY_RANGE_TO_SET_LUA = ScriptLoad
				.loadScript(" local elements = redis.call('zrangebyscore', KEYS[1], ARGV[1], ARGV[2]);"
						+ " if elements and table.getn(elements) > 0 then "
						+ " redis.call('sadd', KEYS[2], unpack(elements)) end; return true;");
	}

	private JedisPool jedisPool;

	public RedisTemplate(JedisPool jedisPool) {

		this.jedisPool = jedisPool;
	}

	private String getLockKey(String key) {

		return "JedisLocked_" + key;
	}
	
	
	public void copySortSetRangeToSet(String sortSetKey, int min, int max,
			String setKey) {
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			COPY_RANGE_TO_SET_LUA.execute(jedis,
					Arrays.asList(sortSetKey, setKey),
					Arrays.asList(min + "", max + ""), false);

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}
	}

	/**
	 * 获取锁
	 * 
	 * @param key
	 *            锁键
	 * @param value
	 *            锁的值。是一个唯一的值，每次调用应该不同
	 * @param lockTime
	 *            锁定的时间。单位是秒
	 * @param waitTime
	 *            等待的时间。单位是秒
	 * @return 获取到锁，返回一个随机字符串，否则返回null
	 * @author wei.ss
	 */
	public String acquireLock(String key, int lockTime, int waitTime) {

		String value = UUIDHexGenerator.generate();
		key = getLockKey(key);
		long begin = System.currentTimeMillis();
		long endTime = begin + waitTime * 1000;

		boolean locked = false;
		String result = null;
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();

			while (System.currentTimeMillis() < endTime) {
				result = jedis.set(key, value, "NX", "EX", lockTime);
				if ("OK".equals(result)) {
					locked = true;
					break;
				}
				TimeUnit.MILLISECONDS.sleep(10);
			}
		} catch (Exception e) {
			e.printStackTrace(); // 获取分布式锁发生异常
		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}

		return locked ? value : null;
	}

	/**
	 * 释放锁
	 * 
	 * @param key
	 * @param value
	 * @author wei.ss
	 */
	public void releaseLock(String key, String value) {

		key = getLockKey(key);
		Transaction transaction = null;
		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			while (true) {
				jedis.watch(key); // 监控key
				String oldValue = jedis.get(key);
				if (oldValue == null || !value.equals(oldValue)) {
					jedis.unwatch();
					// 锁已经过期了，或者被其他客户端获取了
					return;
				}

				transaction = jedis.multi();// 开启事务
				transaction.del(key);
				List<Object> result = transaction.exec(); // 提交事务
				if (null == result || result.isEmpty()) {
					// 事务执行失败，重试
					continue;
				}
			}
		} catch (Exception e) {
			// 释放锁失败
			e.printStackTrace();
		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}
	}

	/**
	 * 使用lua脚本释放锁
	 * 
	 * @param key
	 * @param value
	 */
	public void releaseLockWithLua(String key, String value) {
		key = getLockKey(key);

		Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			RELEASE_LOCK_LUA.execute(jedis, Arrays.asList(key),
					Arrays.asList(value), false);

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}
	}

}
